@sys.description('this is deployTimeSuffix param')
param deployTimeSuffix string = newGuid()

@sys.description('this module a')
module modATest './modulea.bicep' = {
  name: 'modATest'
  params: {
    stringParamB: 'hello!'
    objParam: {
      a: 'b'
    }
    arrayParam: [
      {
        a: 'b'
      }
      'abc'
    ]
  }
}

@sys.description('this module b')
module modB './child/moduleb.bicep' = {
  name: 'modB'
  params: {
    location: 'West US'
  }
}

@sys.description('this is just module b with a condition')
module modBWithCondition './child/moduleb.bicep' = if (1 + 1 == 2) {
  name: 'modBWithCondition'
  params: {
    location: 'East US'
  }
}

module modBWithCondition2 './child/moduleb.bicep' =
  // awkward comment
  if (1 + 1 == 2) {
    name: 'modBWithCondition2'
    params: {
      location: 'East US'
    }
  }

module modC './child/modulec.json' = {
  name: 'modC'
  params: {
    location: 'West US'
  }
}

module modCWithCondition './child/modulec.json' = if (2 - 1 == 1) {
  name: 'modCWithCondition'
  params: {
    location: 'East US'
  }
}

module optionalWithNoParams1 './child/optionalParams.bicep' = {
  name: 'optionalWithNoParams1'
}

module optionalWithNoParams2 './child/optionalParams.bicep' = {
  name: 'optionalWithNoParams2'
  params: {}
}

module optionalWithAllParams './child/optionalParams.bicep' = {
  name: 'optionalWithNoParams3'
  params: {
    optionalString: 'abc'
    optionalInt: 42
    optionalObj: {}
    optionalArray: []
  }
}

resource resWithDependencies 'Mock.Rp/mockResource@2020-01-01' = {
  name: 'harry'
  properties: {
    modADep: modATest.outputs.stringOutputA
    modBDep: modB.outputs.myResourceId
    modCDep: modC.outputs.myResourceId
  }
}

module optionalWithAllParamsAndManualDependency './child/optionalParams.bicep' = {
  name: 'optionalWithAllParamsAndManualDependency'
  params: {
    optionalString: 'abc'
    optionalInt: 42
    optionalObj: {}
    optionalArray: []
  }
  dependsOn: [
    resWithDependencies
    optionalWithAllParams
  ]
}

module optionalWithImplicitDependency './child/optionalParams.bicep' = {
  name: 'optionalWithImplicitDependency'
  params: {
    optionalString: concat(resWithDependencies.id, optionalWithAllParamsAndManualDependency.name)
    optionalInt: 42
    optionalObj: {}
    optionalArray: []
  }
}

module moduleWithCalculatedName './child/optionalParams.bicep' = {
  name: '${optionalWithAllParamsAndManualDependency.name}${deployTimeSuffix}'
  params: {
    optionalString: concat(resWithDependencies.id, optionalWithAllParamsAndManualDependency.name)
    optionalInt: 42
    optionalObj: {}
    optionalArray: []
  }
}

resource resWithCalculatedNameDependencies 'Mock.Rp/mockResource@2020-01-01' = {
  name: '${optionalWithAllParamsAndManualDependency.name}${deployTimeSuffix}'
  properties: {
    modADep: moduleWithCalculatedName.outputs.outputObj
  }
}

output stringOutputA string = modATest.outputs.stringOutputA
output stringOutputB string = modATest.outputs.stringOutputB
output objOutput object = modATest.outputs.objOutput
output arrayOutput array = modATest.outputs.arrayOutput
output modCalculatedNameOutput object = moduleWithCalculatedName.outputs.outputObj

/*
  valid loop cases
*/

@sys.description('this is myModules')
var myModules = [
  {
    name: 'one'
    location: 'eastus2'
  }
  {
    name: 'two'
    location: 'westus'
  }
]

var emptyArray = []

// simple module loop
module storageResources 'modulea.bicep' = [
  for module in myModules: {
    name: module.name
    params: {
      arrayParam: []
      objParam: module
      stringParamB: module.location
    }
  }
]

// simple indexed module loop
module storageResourcesWithIndex 'modulea.bicep' = [
  for (module, i) in myModules: {
    name: module.name
    params: {
      arrayParam: [
        i + 1
      ]
      objParam: module
      stringParamB: module.location
      stringParamA: concat('a', i)
    }
  }
]

// nested module loop
module nestedModuleLoop 'modulea.bicep' = [
  for module in myModules: {
    name: module.name
    params: {
      arrayParam: [for i in range(0, 3): concat('test-', i, '-', module.name)]
      objParam: module
      stringParamB: module.location
    }
  }
]

// duplicate identifiers across scopes are allowed (inner hides the outer)
module duplicateIdentifiersWithinLoop 'modulea.bicep' = [
  for x in emptyArray: {
    name: 'hello-${x}'
    params: {
      objParam: {}
      stringParamA: 'test'
      stringParamB: 'test'
      arrayParam: [for x in emptyArray: x]
    }
  }
]

// duplicate identifiers across scopes are allowed (inner hides the outer)
var duplicateAcrossScopes = 'hello'
module duplicateInGlobalAndOneLoop 'modulea.bicep' = [
  for duplicateAcrossScopes in []: {
    name: 'hello-${duplicateAcrossScopes}'
    params: {
      objParam: {}
      stringParamA: 'test'
      stringParamB: 'test'
      arrayParam: [for x in emptyArray: x]
    }
  }
]

var someDuplicate = true
var otherDuplicate = false
module duplicatesEverywhere 'modulea.bicep' = [
  for someDuplicate in []: {
    name: 'hello-${someDuplicate}'
    params: {
      objParam: {}
      stringParamB: 'test'
      arrayParam: [for otherDuplicate in emptyArray: '${someDuplicate}-${otherDuplicate}']
    }
  }
]

module propertyLoopInsideParameterValue 'modulea.bicep' = {
  name: 'propertyLoopInsideParameterValue'
  params: {
    objParam: {
      a: [for i in range(0, 10): i]
      b: [for i in range(1, 2): i]
      c: {
        d: [for j in range(2, 3): j]
      }
      e: [
        for k in range(4, 4): {
          f: k
        }
      ]
    }
    stringParamB: ''
    arrayParam: [
      {
        e: [for j in range(7, 7): j]
      }
    ]
  }
}

module propertyLoopInsideParameterValueWithIndexes 'modulea.bicep' = {
  name: 'propertyLoopInsideParameterValueWithIndexes'
  params: {
    objParam: {
      a: [for (i, i2) in range(0, 10): i + i2]
      b: [for (i, i2) in range(1, 2): i / i2]
      c: {
        d: [for (j, j2) in range(2, 3): j * j2]
      }
      e: [
        for (k, k2) in range(4, 4): {
          f: k
          g: k2
        }
      ]
    }
    stringParamB: ''
    arrayParam: [
      {
        e: [for j in range(7, 7): j]
      }
    ]
  }
}

module propertyLoopInsideParameterValueInsideModuleLoop 'modulea.bicep' = [
  for thing in range(0, 1): {
    name: 'propertyLoopInsideParameterValueInsideModuleLoop'
    params: {
      objParam: {
        a: [for i in range(0, 10): i + thing]
        b: [for i in range(1, 2): i * thing]
        c: {
          d: [for j in range(2, 3): j]
        }
        e: [
          for k in range(4, 4): {
            f: k - thing
          }
        ]
      }
      stringParamB: ''
      arrayParam: [
        {
          e: [for j in range(7, 7): j % (thing + 1)]
        }
      ]
    }
  }
]

// BEGIN: Key Vault Secret Reference

resource kv 'Microsoft.KeyVault/vaults@2019-09-01' existing = {
  name: 'testkeyvault'
}

module secureModule1 'child/secureParams.bicep' = {
  name: 'secureModule1'
  params: {
    secureStringParam1: kv.getSecret('mySecret')
    secureStringParam2: kv.getSecret('mySecret', 'secretVersion')
  }
}

resource scopedKv 'Microsoft.KeyVault/vaults@2019-09-01' existing = {
  name: 'testkeyvault'
  scope: resourceGroup('otherGroup')
}

module secureModule2 'child/secureParams.bicep' = {
  name: 'secureModule2'
  params: {
    secureStringParam1: scopedKv.getSecret('mySecret')
    secureStringParam2: scopedKv.getSecret('mySecret', 'secretVersion')
  }
}

//looped module with looped existing resource (Issue #2862)
var vaults = [
  {
    vaultName: 'test-1-kv'
    vaultRG: 'test-1-rg'
    vaultSub: 'abcd-efgh'
  }
  {
    vaultName: 'test-2-kv'
    vaultRG: 'test-2-rg'
    vaultSub: 'ijkl-1adg1'
  }
]
var secrets = [
  {
    name: 'secret01'
    version: 'versionA'
  }
  {
    name: 'secret02'
    version: 'versionB'
  }
]

resource loopedKv 'Microsoft.KeyVault/vaults@2019-09-01' existing = [
  for vault in vaults: {
    name: vault.vaultName
    scope: resourceGroup(vault.vaultSub, vault.vaultRG)
  }
]

module secureModuleLooped 'child/secureParams.bicep' = [
  for (secret, i) in secrets: {
    name: 'secureModuleLooped-${i}'
    params: {
      secureStringParam1: loopedKv[i].getSecret(secret.name)
      secureStringParam2: loopedKv[i].getSecret(secret.name, secret.version)
    }
  }
]

module secureModuleCondition 'child/secureParams.bicep' = {
  name: 'secureModuleCondition'
  params: {
    secureStringParam1: true ? kv.getSecret('mySecret') : 'notTrue'
    secureStringParam2: true ? false ? 'false' : kv.getSecret('mySecret', 'secretVersion') : 'notTrue'
  }
}

// END: Key Vault Secret Reference

module withSpace 'module with space.bicep' = {
  name: 'withSpace'
}

module folderWithSpace 'child/folder with space/child with space.bicep' = {
  name: 'childWithSpace'
}

// nameof

var nameofModule = nameof(folderWithSpace)
var nameofModuleParam = nameof(secureModuleCondition.outputs.exposedSecureString)

module moduleWithNameof 'modulea.bicep' = {
  name: 'nameofModule'
  scope: resourceGroup(nameof(nameofModuleParam))
  params: {
    stringParamA: nameof(withSpace)
    stringParamB: nameof(folderWithSpace)
    objParam: {
      a: nameof(secureModuleCondition.outputs.exposedSecureString)
    }
    arrayParam: [
      nameof(vaults)
    ]
  }
}

module moduleWithNullableOutputs 'child/nullableOutputs.bicep' = {
  name: 'nullableOutputs'
}

output nullableString string? = moduleWithNullableOutputs.outputs.?nullableString
output deeplyNestedProperty string? = moduleWithNullableOutputs.outputs.?nullableObj.deeply.nested.property
output deeplyNestedArrayItem string? = moduleWithNullableOutputs.outputs.?nullableObj.deeply.nested.array[0]
output deeplyNestedArrayItemFromEnd string? = moduleWithNullableOutputs.outputs.?nullableObj.deeply.nested.array[^1]
output deeplyNestedArrayItemFromEndAttempt string? = moduleWithNullableOutputs.outputs.?nullableObj.deeply.nested.array[?^1]
